<!--
  弹框组件
  @author 邵鹏坤 2022年9月19日
  UI参考window11 文件管理器：
    1 顶部：
      1.1 左侧：显示导航的图标和导航名
      1.2 右侧：显示‘最小化，最大化，关闭’三个功能按钮
    2 中间主题部分:
      ...
  1 数据源：
  2 状态：
    最大化：沾满除任务栏以外的其他空间
    窗口化：未全屏并且未隐藏的状态
    最小化：利用v-show进行隐藏，但不销毁，可再次恢复到窗口或最大化状态（根据历史记录，即恢复到最小化之前的状态）
    销毁状态: 无需过多处理，删除vuex中的该弹框，在桌面导航组件内使用v-for自动销毁
  3 交互 ：
    3.1 点击事件：
      3.1.1 最小化：
        通知vuex修改该弹框的状态status
      3.1.1 最大化：
        通知vuex修改该弹框的状态status
      3.1.1 关闭：
        通知vuex在aliveBarList移除该弹框
    3.2 拖拽：
      持续点击header区域即可实现拖拽，窗口不允许拖拽出屏幕上，左，右边缘，底部不允许超过任务栏
    3.3 放大缩小
      持续点击footer区域中右下角的‘》’按钮，即可放大或缩小窗口
  4 主体展示区域（main）
    使用插槽暴露，以便于自定义展示
-->
<template>
  <div
    ref="dialog"
    :class="un.p('container')"
    v-show="aliveBar && aliveBar?.status !== SMALLSREEN && aliveBar?.alive"
    :style="parseStyle(aliveBar)"
    @mousedown="aliveBar && dialogClick(aliveBar.id, aliveBar.zindex)"
  >
    <div :class="un.p('header')">
      <div :class="un.p('header-title')" @mousedown="dragDown">
        <div :class="un.p('header-title-left')">
          <div :class="un.p('header-title-icon')">
            <Icon :iconClass="aliveBar?.icon"></Icon>
          </div>
          <div :class="un.p('header-title-msg')">
            <span>{{ aliveBar?.name }}</span>
            <span :class="un.p('header-title-line')">|</span>
          </div>
        </div>
        <div :class="un.p('header-title-right')">
          <button class="btn zuixiaohua" @click.stop="smallDialog" @mousedown.stop="null">
            <Icon iconClass="icon-zuixiaohua"></Icon>
          </button>
          <button class="btn zuidahua" @click.stop="maxDialog" @mousedown.stop="null">
            <Icon iconClass="icon-zuidahua"></Icon>
          </button>
          <button class="btn guanbi" @click.stop="closeDialog" @mousedown.stop="null">
            <Icon iconClass="icon-guanbi"></Icon>
          </button>
        </div>
      </div>
      <div class="dialog-header-operationBar"></div>
    </div>
    <div :class="un.p('body')">
      <div class="dialog-body-main">
        <KeepAlive>
          <component :is="mainComponent" />
        </KeepAlive>
      </div>
    </div>
    <div :class="un.p('footer')">
      <div :class="un.p('footer-left')"></div>
      <div :class="un.p('footer-right')">
        <div class="resizeBox" ref="resizeBox" @mousedown="resizeDown" v-if="!(aliveBar?.status === MAXSREEN)"></div>
      </div>
    </div>
  </div>
</template>
<script lang="ts" setup>
  import useNamespace from '@/hooks/use-namespace'
  import { ref, shallowRef, reactive, onBeforeMount, defineAsyncComponent, provide } from 'vue'
  import { useStore } from 'vuex'
  import Icon from '@/views/icon/Icon.vue'
  import { MAXSREEN, NORMAL, SMALLSREEN } from '@/assets/js/dialogStatus'
  import { AliveDialog } from '@/winTypes'
  const un = useNamespace('dialog')
  // 使用props接收该导航的id
  const props = defineProps<{
    id: number
  }>()
  const store = useStore()
  // 根据该导航id从vuex中拿到已经初始化的导航对象（里面包含需要渲染该弹框的初始数据）
  const aliveBar: AliveDialog = reactive(store.getters.getAliveBar(props.id))
  const dialog = ref()
  const resizeBox = ref()
  const mainComponent = shallowRef()
  // 拖拽状态：0：鼠标未按下（未开始拖拽）
  // 1：鼠标按下（开始拖拽）
  // 2：鼠标移动（开始移动）
  const dragFlag = ref(0)
  // 放大缩小的状态：0：鼠标未按下（未开始）
  // 1：鼠标按下（开始拖拽）
  // 2：鼠标移动（开始移动）
  const resizeFlag = ref(0)
  // 点击位置距离弹框左上角的距离
  const diffX = ref(0)
  const diffY = ref(0)
  const dragLeft = ref(0)
  const dragTop = ref(0)
  const dialogWidth = ref(0)
  const dialogHeight = ref(0)
  const deafultWidth = ref(0)
  const deafultHeight = ref(0)
  /**
   * 在挂载完成后，保存该导航的id，将弹框和导航一对一绑定在一起
   */
  onBeforeMount(() => {
    if (aliveBar.width && aliveBar.height) {
      deafultWidth.value = aliveBar.width
      deafultHeight.value = aliveBar.height
    }
    provide('dialog', aliveBar)
    mainComponent.value = getCom(aliveBar)
  })
  /**
   * 动态获取一个组件
   */
  const getCom = (aliveBar: AliveDialog | undefined) => {
    return defineAsyncComponent(() => import('@/views' + aliveBar?.route + '/example.vue'))
  }
  /**
   *
   */
  const parseStyle = (aliveBar: AliveDialog | undefined) => {
    if (!aliveBar) return
    if (aliveBar.status === MAXSREEN) {
      // 如果该窗口处于最大化

      return {
        width: '100%',
        height: '94.5vh',
        // 取初始化好的zindex
        'z-index': aliveBar.zindex,
        top: '0%',
        left: '0%',
        'box-shadow': '0px 0px 2px 0px #4e4e4e',
        'border-radius': '0px'
      }
    } else if (aliveBar.status === NORMAL) {
      // 如果该窗口处于窗口化,则取vuex内初始化的默认窗口大小
      // 未开始拖拽
      if (dragFlag.value === 0) {
        if (resizeFlag.value === 0) {
          return {
            width: aliveBar.width + 'px',
            height: aliveBar.height + 'px',
            left: aliveBar.left + 'px',
            top: aliveBar.top + 'px',
            // 'transition-property': 'width, height,top,left',
            // 取初始化好的zindex
            'z-index': aliveBar.zindex
          }
        } else {
          // 开始缩放
          return {
            width: aliveBar.width + 'px',
            height: aliveBar.height + 'px',
            left: aliveBar.left + 'px',
            top: aliveBar.top + 'px',
            'transition-property': 'none',
            // 取初始化好的zindex
            'z-index': aliveBar.zindex
          }
        }
      } else {
        // 开始拖拽
        return {
          width: aliveBar.width + 'px',
          height: aliveBar.height + 'px',
          left: aliveBar.left + 'px',
          top: aliveBar.top + 'px',
          'transition-property': 'width, height',
          // 取初始化好的zindex
          'z-index': aliveBar.zindex
        }
      }
    } else if (aliveBar.status === SMALLSREEN) {
      if (aliveBar.statusHistory === MAXSREEN) {
        return {
          width: '100%',
          height: '94.5vh',
          // 取初始化好的zindex
          'z-index': aliveBar.zindex,
          'box-shadow': '0px 0px 2px 0px #4e4e4e',
          'border-radius': '0px'
        }
      } else {
        return {
          width: aliveBar.width,
          height: aliveBar.height,
          left: aliveBar.left + 'px',
          top: aliveBar.top + 'px',
          // 取初始化好的zindex
          'z-index': aliveBar.zindex
        }
      }
    }
  }
  /**
   * 最小化按钮单击回调
   */
  const smallDialog = () => {
    store.commit('smallDialog', aliveBar.id)
  }
  /**
   * 全屏按钮单击回调
   */
  const maxDialog = () => {
    store.commit('maxDialog', aliveBar.id)
    store.commit('setBigZIndex', aliveBar)
  }
  /**
   * 关闭按钮单击回调
   */
  const closeDialog = () => {
    store.commit('closeDialog', aliveBar.id)
  }
  /**
   * 弹框的单击事件，用于切换zindex层级以达到展示焦点弹框的目的
   * @param id
   * @param zindex
   */
  const dialogClick = (id: number, zindex: number) => {
    // 通知store
    store.commit('setBigZIndex', aliveBar)
  }

  /**
   * 弹框的头部拖拽功能-鼠标按下
   * @param event
   */
  const dragDown = (event: any) => {
    if (aliveBar.status === MAXSREEN) return
    var event = event || window.event //兼容IE浏览器
    const drag = dialog.value
    //    鼠标点击物体那一刻相对于物体左侧边框的距离=点击时的位置相对于浏览器最左边的距离-物体左边框相对于浏览器最左边的距离
    diffX.value = event.clientX - drag.offsetLeft
    diffY.value = event.clientY - drag.offsetTop
    if (typeof drag.setCapture !== 'undefined') {
      // 该函数在属于当前线程的指定窗口里设置鼠标捕获。
      // 一旦窗口捕获了鼠标，所有鼠标输入都针对该窗口，无论光标是否在窗口的边界内。
      // 同一时刻只能有一个窗口捕获鼠标。
      // 如果鼠标光标在另一个线程创建的窗口上，只有当鼠标键按下时系统才将鼠标输入指向指定的窗口。
      drag.setCapture()
    }
    drag.style.left = aliveBar.left + 'px'
    drag.style.top = aliveBar.top + 'px'
    dragFlag.value = 1
    drag.style.left = aliveBar.left + 'px'
    drag.style.top = aliveBar.top + 'px'
    dragUp()
    dragMove()
  }
  const dragMouseMove = (event: any) => {
    if (dragFlag.value === 0) return
    var event = event || window.event
    const drag = dialog.value
    let moveX = event.clientX - diffX.value
    let moveY = event.clientY - diffY.value
    if (moveX < 0) {
      moveX = 0
    } else if (moveX > window.innerWidth - drag.offsetWidth) {
      moveX = window.innerWidth - drag.offsetWidth
    }
    if (moveY < 0) {
      moveY = 0
    } else if (moveY > window.innerHeight - drag.offsetHeight - window.innerHeight * 0.055) {
      moveY = window.innerHeight - drag.offsetHeight - window.innerHeight * 0.055
    }
    drag.style.left = moveX + 'px'
    drag.style.top = moveY + 'px'
    dragLeft.value = moveX
    dragTop.value = moveY
    if (dragFlag.value != 2) {
      dragFlag.value = 2
    }
  }
  /**
   * 弹框的头部拖拽功能-鼠标移动
   * @param event
   */
  const dragMove = () => {
    window.addEventListener('mousemove', dragMouseMove)
  }
  /**
   * 弹框的头部拖拽功能-鼠标松开
   * @param e
   * @param zindex
   */
  const dragUp = () => {
    const mouseup = () => {
      if (dragFlag.value !== 2) return (dragFlag.value = 0)

      if ((aliveBar.left && aliveBar.top) || aliveBar.left === 0 || aliveBar.top === 0) {
        aliveBar.left = dragLeft.value
        aliveBar.top = dragTop.value
      }
      const drag = dialog.value
      //修复低版本ie bug
      if (typeof drag.releaseCapture != 'undefined') {
        drag.releaseCapture()
      }
      dragFlag.value = 0
      window.removeEventListener('mouseup', mouseup)
      window.removeEventListener('mousemove', dragMouseMove)
      return
    }
    window.addEventListener('mouseup', mouseup)
  }
  /**
   * 弹框右下角放大缩小窗口-鼠标按下
   * @param event
   */
  const resizeDown = (event: any) => {
    if (aliveBar.status === MAXSREEN) return
    var event = event || window.event //兼容IE浏览器
    const drag = dialog.value
    dialogWidth.value = drag.offsetWidth
    dialogHeight.value = drag.offsetHeight
    //    鼠标点击物体那一刻相对于物体左侧边框的距离=点击时的位置相对于浏览器最左边的距离-物体左边框相对于浏览器最左边的距离
    diffX.value = event.clientX
    diffY.value = event.clientY
    if (typeof drag.setCapture !== 'undefined') {
      // 该函数在属于当前线程的指定窗口里设置鼠标捕获。
      // 一旦窗口捕获了鼠标，所有鼠标输入都针对该窗口，无论光标是否在窗口的边界内。
      // 同一时刻只能有一个窗口捕获鼠标。
      // 如果鼠标光标在另一个线程创建的窗口上，只有当鼠标键按下时系统才将鼠标输入指向指定的窗口。
      drag.setCapture()
    }
    if (aliveBar.left && aliveBar.top) {
      drag.style.left = aliveBar.left + 'px'
      drag.style.top = aliveBar.top + 'px'
    }
    resizeFlag.value = 1
    if (aliveBar.left && aliveBar.top) {
      drag.style.left = aliveBar.left + 'px'
      drag.style.top = aliveBar.top + 'px'
    }
    resizeMove()
    resizeUp()
  }
  /**
   * 弹框放大缩小功能-鼠标移动
   */
  const resizeMove = () => {
    window.addEventListener('mousemove', mouseMove)
  }
  const mouseMove = (event: any) => {
    if (resizeFlag.value === 0) return
    var event = event || window.event
    const drag = dialog.value
    if (event.clientX >= window.innerWidth) {
      dragLeft.value = window.innerWidth - diffX.value
    } else {
      dragLeft.value = event.clientX - diffX.value
    }
    if (event.clientY >= window.innerHeight - window.innerHeight * 0.055) {
      dragTop.value = window.innerHeight - diffY.value - window.innerHeight * 0.055
    } else {
      dragTop.value = event.clientY - diffY.value
    }
    if (dragLeft.value > deafultWidth.value - dialogWidth.value) {
      drag.style.width = dragLeft.value + dialogWidth.value + 'px'
    }
    if (dragTop.value > deafultHeight.value - dialogHeight.value) {
      drag.style.height = dragTop.value + dialogHeight.value + 'px'
    }
    if (resizeFlag.value != 2) {
      resizeFlag.value = 2
    }
  }
  /**
   * 弹框放大缩小功能-鼠标松开
   */
  const resizeUp = () => {
    function mouseup() {
      if (resizeFlag.value !== 2) return (resizeFlag.value = 0)
      const drag = dialog.value
      if (dragLeft.value >= deafultWidth.value - dialogWidth.value) {
        if (aliveBar.width) {
          aliveBar.width = Number(drag.style.width.substring(0, drag.style.width.indexOf('px')))
        }
      } else {
        if (aliveBar) {
          aliveBar.width = deafultWidth.value
        }
      }
      if (dragTop.value >= deafultHeight.value - dialogHeight.value) {
        aliveBar.height = Number(drag.style.height.substring(0, drag.style.height.indexOf('px')))
      } else {
        aliveBar.height = deafultHeight.value
      }
      //修复低版本ie bug
      if (typeof drag.releaseCapture != 'undefined') {
        drag.releaseCapture()
      }
      resizeFlag.value = 0
      window.removeEventListener('mouseup', mouseup)
      window.removeEventListener('mousemove', mouseMove)
      return
    }
    window.addEventListener('mouseup', mouseup)
  }
</script>

<style lang="scss">
  @import './css/index.scss';
</style>
